package com.itheima.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


/**
 *1. 连接点：JoinPoint，可以被AOP控制的方法（暗含方法执行时的相关信息）
 *连接点指的是可以被aop控制的方法。例如：入门程序当中所有的业务方法都是可以被aop控制的方法。
 *
 *2. 通知：Advice，指哪些重复的逻辑，也就是共性功能（最终体现为一个方法）
 *在入门程序中是需要统计各个业务方法的执行耗时的，此时我们就需要在这些业务方法运行开始之前，先记录这个方法运行的开始时间，在每一个业务方法运行结束的时候，再来记录这个方法运行的结束时间。
 *但是在AOP面向切面编程当中，我们只需要将这部分重复的代码逻辑抽取出来单独定义。抽取出来的这一部分重复的逻辑，也就是共性的功能。
 *
 *3. 切入点：PointCut，匹配连接点的条件，通知仅会在切入点方法执行时被应用
 * 在通知当中，我们所定义的共性功能到底要应用在哪些方法上？此时就涉及到了切入点pointcut概念。切入点指的是匹配连接点的条件。通知仅会在切入点方法运行时才会被应用。
 * 	在aop的开发当中，我们通常会通过一个切入点表达式来描述切入点
 *
 *4. 切面：Aspect，描述通知与切入点的对应关系（通知+切入点）
 *当通知和切入点结合在一起，就形成了一个切面。通过切面就能够描述当前aop程序需要针对于哪个原始方法，在什么时候执行什么样的操作。
 *
 *5. 目标对象：Target，通知所应用的对象
 *目标对象指的就是通知所应用的对象，我们就称之为目标对象。
 *
 *指定对哪些特定的方法编程,第一个*是方法的返回值，第二个*是类名，第三个*是方法名
 */
@Slf4j
@Component
@Aspect
public class MyAspect1 {

    /*
    切入点表达式重复:抽取。切入点方法（公共的切入点表达式）
    切入点方法使用private修饰时，仅能在当前切面类中引用该表达式。
    当外部其他切面类中也要引用当前类中的切入点表达式，就需要把private改为public，而在引用的时候，具体的语法为：全类名.方法名()，eg:@Before("com.itheima.aspect.MyAspect1.pt()")
     */
    @Pointcut("execution(* com.itheima.service.*.*(..))")
    private void pt(){

    }

    //前置通知，此注解标注的通知方法在目标方法前被执行
//    @Before("execution(* com.itheima.service.*.*(..))")
    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info("before ...");

    }

    //环绕通知，此注解标注的通知方法在目标方法前、后都被执行
    @Around("execution(* com.itheima.service.*.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("around before ...");

        //调用目标对象的原始方法执行
        Object result = proceedingJoinPoint.proceed();
        
        //原始方法如果执行时有异常，环绕通知中的后置代码不会在执行了
        
        log.info("around after ...");
        return result;
    }

    //后置通知，此注解标注的通知方法在目标方法后被执行，无论是否有异常都会执行
    @After("execution(* com.itheima.service.*.*(..))")
    public void after(JoinPoint joinPoint){
        log.info("after ...");
    }

    //返回后通知（程序在正常执行的情况下，会执行的后置通知）,此注解标注的通知方法在目标方法后被执行，有异常不会执行
    @AfterReturning("execution(* com.itheima.service.*.*(..))")
    public void afterReturning(JoinPoint joinPoint){
        log.info("afterReturning ...");
    }

    //异常通知（程序在出现异常的情况下，执行的后置通知），此注解标注的通知方法发生异常后执行
    @AfterThrowing("execution(* com.itheima.service.*.*(..))")
    public void afterThrowing(JoinPoint joinPoint){
        log.info("afterThrowing ...");
    }
}